import threading
from tkinter import *
from tkinter import filedialog

import keyboard
from PIL import ImageTk, Image, ImageOps
import pyautogui
from numpy import asarray
from win10toast import ToastNotifier

class Picture(object):
    def __init__(self):
        self.source = Image.new(mode="RGB", size=(0, 0))
        self.source_preview = Image.new(mode="RGB", size=(0, 0))
        self.thresh_preview = Image.new(mode="RGB", size=(0, 0))


class Settings(object):
    def __init__(self):
        self.RGB = None
        self.size = None
        self.resolution = None
        self.armed = False
        self.threshold = None
        self.status = False
        self.toaster = ToastNotifier()


# 初始化窗口
def loadPic():
    global img

    # 加载图像并调整大小以适合窗口
    filename = filedialog.askopenfilename(initialdir="/",
                                          title="选择一个文件",
                                          filetypes=(("all files", "*.*"), ("jpeg files", "*.jpg")))
    pic.source = Image.open(filename).convert("RGB")

    adj = max(pic.source.width, pic.source.height) / 250
    wAdj = int(pic.source.width / adj)
    hAdj = int(pic.source.height / adj)
    settings.size = (wAdj, hAdj)

    pic.source_preview = pic.source.resize((wAdj, hAdj), 4)
    img = ImageTk.PhotoImage(pic.source_preview)

    # 配置框架列宽
    for x in range(0, 4):
        topFrame.columnconfigure(x, weight=1)
        midFrame.columnconfigure(x, weight=1, minsize=125)
        botFrame.columnconfigure(x, weight=1, minsize=125)

    # topFrame 布局
    topFrame.grid_propagate(0)
    topFrame.config(width=500, height=45)
    ts.config(text='')
    load.config(text='替换图像')

    load.grid(column=1, columnspan=2)
    l1.place(x=-30 + pic.source_preview.width / 2, y=20)
    l2.place(x=465 - (pic.source_preview.width / 2), y=20)

    # 画布布局
    canvas.config(width=500, height=int(pic.source.height / adj))
    canvas.pack()
    canvas.delete("source")
    canvas.create_image(0, 0, anchor=NW, image=img, tag="source")

    # 中框布局
    midFrame.grid_propagate(0)
    midFrame.config(width=500, height=115)
    Label(midFrame, text="图像设置", font='bold').grid(row=0, column=1, columnspan=2)

    Label(midFrame, text="颜色").grid(row=1, column=0)
    w1.grid(row=2, column=0)

    Label(midFrame, text="临界点").grid(row=1, column=1)
    w2.grid(row=2, column=1)
    w2.set(125)

    Label(midFrame, text="像素").grid(row=1, column=2)
    w3.grid(row=2, column=2)
    w3.set(100)

    Button(midFrame, text='反转图像', command=lambda: invertPreview()).grid(row=2, column=3)

    # botFrame布局
    botFrame.grid_propagate(0)
    botFrame.config(width=500, height=140)
    Label(botFrame, text="绘图设置", font='bold').grid(row=0, column=1, columnspan=2)

    Label(botFrame, text="输出图片尺寸").grid(row=1, column=0)
    Label(botFrame, text="W:").grid(row=2, column=0, sticky=W, padx=33)
    Label(botFrame, text="H:").grid(row=3, column=0, sticky=W, padx=35)
    sizeW.grid(row=2, column=0, sticky=E, padx=(0, 35))
    sizeH.grid(row=3, column=0, sticky=E, padx=(0, 35))
    sizeW.delete(0, "end")
    sizeW.insert(END, wAdj)
    sizeH.delete(0, "end")
    sizeH.insert(END, hAdj)
    sizeSet.grid(row=4, column=0, sticky=W, padx=25, pady=4)
    sizeReset.grid(row=4, column=0, sticky=E, padx=(0, 20), pady=4)

    Label(botFrame, text="像素大小").grid(row=1, column=1)
    pxSize.grid(row=2, column=1)
    pxSize.delete(0, "end")
    pxSize.insert(END, 1)
    Label(botFrame, text="绘画速度").grid(row=1, column=2, padx=20)
    speedSet.grid(row=2, column=2)

    arm.grid(row=2, column=3)

    # 使用默认设置生成初始预览
    show_values(None)


# 选择后展示图片
def show_values(event):
    global img2
    imSmall = pic.source_preview
    settings.threshold = w2.get()
    settings.resolution = w3.get()

    # 调整图像分辨率
    if settings.resolution < 100:
        width, height = pic.source_preview.size
        smallWidth = int(width * (settings.resolution / 100))
        smallHeight = int(height * (settings.resolution / 100))

        lowRes = pic.source_preview.resize((smallWidth, smallHeight))
        imSmall = lowRes.resize((width, height), 4)

    # 阈值图像
    if RGBvar.get() == "全部":
        im2 = imSmall.convert('L').point(lambda p: p > settings.threshold and 255)
    else:
        if RGBvar.get() == "红色":
            settings.RGB = 0
        elif RGBvar.get() == "绿色":
            settings.RGB = 1
        elif RGBvar.get() == "蓝色":
            settings.RGB = 2

        rgb = imSmall.split()
        im2 = rgb[settings.RGB].point(lambda p: p > settings.threshold and 255)

    pic.thresh_preview = im2
    img2 = ImageTk.PhotoImage(im2)

    canvas.delete("preview")
    canvas.create_image(500 - im2.width, 0, anchor=NW, image=img2, tag="preview")


# 更新图片大小
def updateSize():
    width = sizeW_var.get()
    height = sizeH_var.get()

    if width != settings.size[0] and height != settings.size[1]:
        settings.size = (width, height)
    elif width != settings.size[0]:
        adj = settings.size[0] / width
        height = int(settings.size[1] / adj)
        settings.size = (width, height)

        sizeH.delete(0, "end")
        sizeH.insert(END, height)
    elif height != settings.size[1]:
        height = sizeH_var.get()
        adj = settings.size[1] / height
        width = int(settings.size[0] / adj)
        settings.size = (width, height)

        sizeW.delete(0, "end")
        sizeW.insert(END, width)


# 重置图片大小
def resetSize():
    width, height = pic.source_preview.size
    settings.size = (width, height)

    sizeW.delete(0, "end")
    sizeW.insert(END, width)
    sizeH.delete(0, "end")
    sizeH.insert(END, height)


# 设置绘画速度
def setSpeed(speed):
    # 将秒数设置为在每次鼠标移动之间暂停
    if speed == "Fastest":
        pyautogui.PAUSE = 0
    elif speed == "Fast":
        pyautogui.PAUSE = 0.001
    elif speed == "Medium":
        pyautogui.PAUSE = 0.015
    elif speed == "Slow":
        pyautogui.PAUSE = 0.05


# 会话准备设置
def armDrawing():
    if settings.armed:
        arm.config(relief="raised", text="未准备", fg="black")
    else:
        arm.config(relief="sunken", text="已准备", fg="red")

    settings.armed = not settings.armed


def stopPainting():
    settings.status = not settings.status


class MyThreading(threading.Thread):
    def __init__(self):
        super(MyThreading, self).__init__()

    # 线程要运行的代码
    def run(self):
        im2 = pic.thresh_preview.resize(settings.size, 1)
        im2 = im2.convert('L').point(lambda p: p > settings.threshold and 255)

        pxSize = pxSize_var.get()
        settings.toaster.show_toast("开始",
                                    "准备开始绘画!",
                                    icon_path=None,
                                    duration=5,
                                    threaded=True)
        # 使用鼠标自动化绘制图像
        print("状态" + str(settings.armed))
        if settings.armed:
            data = asarray(im2)
            posY = 0
            x, y = pyautogui.position()

            for row in data:
                if settings.status:
                    settings.toaster.show_toast("终止",
                                                "程序已终止绘画",
                                                icon_path=None,
                                                duration=5,
                                                threaded=True)
                    settings.status = not settings.status
                    break
                pyautogui.moveTo(x, y + (posY * pxSize))
                current = row[0]
                run = 0
                for pxNum in range(0, len(row)):
                    if row[pxNum] == current:
                        run += 1
                        if pxNum == len(row) - 1:
                            if current == 0:
                                pyautogui.keyDown('control')
                                pyautogui.drag(run * pxSize, 0, button='left')
                                run = 1
                                current = 255
                                pyautogui.keyUp('control')
                            else:
                                pyautogui.keyDown('control')
                                pyautogui.move(run * pxSize, 0)
                                run = 1
                                current = 0
                                pyautogui.keyUp('control')
                    else:
                        if current == 0:
                            pyautogui.keyDown('control')
                            pyautogui.drag(run * pxSize, 0, button='left')
                            run = 1
                            current = 255
                            pyautogui.keyUp('control')
                        else:
                            pyautogui.keyDown('control')
                            pyautogui.move(run * pxSize, 0)
                            run = 1
                            current = 0
                            pyautogui.keyUp('control')
                posY += 1
            settings.toaster.show_toast("完成",
                                        "绘画已完成",
                                        icon_path=None,
                                        duration=5,
                                        threaded=True)


# 自动会话
def moveMouse():
    t1 = MyThreading()
    t1.start()


# 图片颜色反转
def invertPreview():
    pic.source_preview = ImageOps.invert(pic.source_preview)
    w2.set(abs(255 - w2.get()))
    show_values(None)


if __name__ == '__main__':
    pic = Picture()
    settings = Settings()

    pyautogui.PAUSE = 0.001

    root = Tk()
    root.title('自动绘画@风子')
    # 定义 tkinter 帧
    topFrame = Frame(root)
    topFrame.grid()
    topFrame.grid_propagate(0)
    topFrame.config(width=300, height=100)
    topFrame.grid_columnconfigure(0, weight=1)
    topFrame.grid_rowconfigure(0, weight=1)
    canvasFrame = Frame(root)
    canvasFrame.grid()
    midFrame = Frame(root, border=2, relief='raised')
    midFrame.grid()
    botFrame = Frame(root, border=2, relief='raised')
    botFrame.grid()

    # 创建输入变量
    RGBvar = StringVar()
    sizeH_var = IntVar()
    sizeW_var = IntVar()
    pxSize_var = IntVar()
    speed_var = StringVar()

    # 开始监听键盘输入  <Control-Shift-KeyPress-H>
    keyboard.add_hotkey('ctrl+shift+return', moveMouse)
    keyboard.add_hotkey('ctrl+c', stopPainting)
    # root.bind('<Control-Shift-Return>', lambda event: moveMouse(event))
    # root.bind('<Control-KeyPress-C>', lambda event: stopPainting(event))
    # root.focus_force()

    # Create widgets for each frame
    load = Button(topFrame, text='加载图像', command=loadPic)
    ts = Label(topFrame, text='开始绘画:control+shift+enter;停止绘画:control+c')
    load.grid()
    ts.grid()
    l1 = Label(topFrame, text="原图", font='bold')
    l2 = Label(topFrame, text="预览", font='bold')

    canvas = Canvas(canvasFrame, width=500, height=100)

    w1 = OptionMenu(midFrame, RGBvar, "全部", "红色", "绿色", "蓝色", command=show_values)
    RGBvar.set("全部")
    w2 = Scale(midFrame, from_=0, to=255, length=100, tickinterval=125, orient=HORIZONTAL, command=show_values)
    w3 = Scale(midFrame, from_=1, to=100, length=100, tickinterval=99, orient=HORIZONTAL, command=show_values)

    sizeW = Entry(botFrame, width=5, textvariable=sizeW_var)
    sizeH = Entry(botFrame, width=5, textvariable=sizeH_var)
    sizeSet = Button(botFrame, text="修改", command=updateSize)
    sizeReset = Button(botFrame, text="重置", command=resetSize)
    speedSet = OptionMenu(botFrame, speed_var, "最快的", "快速", "中等的", "慢", command=setSpeed)
    speed_var.set("快速")
    pxSize = Entry(botFrame, width=5, textvariable=pxSize_var)
    arm = Button(botFrame, text="未准备", command=armDrawing)

    mainloop()
